//
//  PeopleModel+Extend.m
//  ReviewDemo
//
//  Created by 龙 on 2018/3/14.
//  Copyright © 2018年 long. All rights reserved.
//

#import "PeopleModel+Extend.h"
#import <objc/runtime.h>
@implementation PeopleModel (Extend)

#pragma mark - 方法交换

/*
 https://www.jianshu.com/p/872447c6dc3f
 
 1、为什么方法交换调用在+load方法中？
     在Objective-C runtime会自动调用两个类方法，分别为+load与+ initialize。
     +load 方法是在类被加载的时候调用的，也就是一定会被调用。
     而+initialize方法是在类或它的子类收到第一条消息之前被调用的，这里所指的消息包括实例方法和类方法的调用。也就是说+initialize方法是以懒加载的方式被调用的，如果程序一直没有给某个类或它的子类发送消息，那么这个类的+initialize方法是永远不会被调用的。
     此外+load方法还有一个非常重要的特性，那就是子类、父类和分类中的+load方法的实现是被区别对待的。换句话说在 Objective-C runtime自动调用+load方法时，分类中的+load方法并不会对主类中的+load方法造成覆盖。
     综上所述，+load 方法是实现 Method Swizzling 逻辑的最佳“场所”
 
 2、为什么方法交换要在dispatch_once中执行？
    方法交换应该要线程安全，而且保证在任何情况下（多线程环境，或者被其他人手动再次调用+load方法）只交换一次，防止再次调用又将方法交换回来。除非只是临时交换使用，在使用完成后又交换回来。
    最常用的解决方案是在+load方法中使用dispatch_once来保证交换是安全的。
    +load方法本身即为线程安全，为什么仍需添加dispatch_once，其原因就在于+load方法本身无法保证其中代码只被执行一次。
 
 3、为什么没有发生死循环？
    为什么sure_viewWillDisappear方法中的代码没有发生递归死循环。
    其原因很简单，因为方法已经执行过交换，调用[self sure_viewWillDisappear:animated]本质是在调用原有方法viewWillDisappear，反而如果我们在方法中调用[self viewWillDisappear:animated]才真的会发生死循环。是不是很绕？仔细看看。
 */


+ (void)load
{
    [super load];
    
    /*
        class_getInstanceMethod     得到类的实例方法
        class_getClassMethod        得到类的类方法
     */
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method oldMethod = class_getInstanceMethod(self, @selector(eatFood:));
        Method newMethod = class_getInstanceMethod(self, @selector(new_EatFood:));
        
        Method oldMethod1 = class_getClassMethod(self, @selector(getClassInfo));
        Method newMethod1 = class_getClassMethod(self, @selector(new_GetClassInfo));
        method_exchangeImplementations(oldMethod, newMethod);//交换方法地址，相当于交换实现方法
        method_exchangeImplementations(oldMethod1, newMethod1);//交换方法地址，相当于交换实现方法

        
    });
    

}

//注意：不能在分类中重写系统方法imageNamed，因为会把系统的功能给覆盖掉
- (void)new_EatFood:(NSString *)food
{
    NSLog(@"%s:%@",__func__,food);
}

+ (void)new_GetClassInfo
{
    NSLog(@"%s:%@",__func__,self);
}

#pragma mark - 动态添加属性

- (void)setAddress:(NSString *)address
{
    objc_setAssociatedObject(self, "address", address, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)address
{
    return objc_getAssociatedObject(self, "address");
}

//- (void)setHeight:(float)height
//{
//    objc_setAssociatedObject(self, "height", @(height), OBJC_ASSOCIATION_ASSIGN);
//}
//
//- (float)height
//{
//    objc_get
//    return objc_getAssociatedObject(self, "height");
//}


#pragma mark - 动态添加方法

void realizeMethod(id self ,SEL _sel, NSString *water){
    NSLog(@"drink %@",water);
};

/*
  这是NSObject根类提供的类方法，调用时机为当被调用的方法实现部分没有找到，而消息转发机制启动之前的这个中间时刻。
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == NSSelectorFromString(@"drink:"))
    {
        /*
         class_addMethod(Class  _Nullable __unsafe_unretained cls, SEL, <#IMP  _Nonnull imp#>, <#const char * _Nullable types#>)

         动态添加run方法
         class: 给哪个类添加方法
         SEL: 添加哪个方法，即添加方法的方法编号
         IMP: 方法实现 => 函数 => 函数入口 => 函数名（添加方法的函数实现（函数地址））
         type: 方法类型，(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
         */
        
        class_addMethod(self, sel, (IMP)realizeMethod, "v@:@");
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}


@end
